package com.util;

import lombok.Getter;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;


@Component
public class RedisUtil {
    //从配置文件中看到 这个连接的redis是10库

    //region   设置注入的redis对象

    //    public RedisUtil(RedisTemplate<String, Object> template) {
//        stringRedisTemplate=template;
    //    }

    @Getter
    private StringRedisTemplate stringRedisTemplate;

    @Resource(name = "defaultRedisTemplate")
    private StringRedisTemplate defaultRedisTemplate;

    @Resource(name = "cacheRedisTemplate")
    private StringRedisTemplate cacheRedisTemplate;

    @Resource(name ="defaultRedisFactory" )
    private JedisConnectionFactory jedisConnectionFactory;


    private static RedisUtil redisUtil;

    /**
     * @PostConstruct：被@PostConstruct修饰的方法会在服务器加载Servle的时候运行，
     * 并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。
     * （PreDestroy（）方法在destroy()方法执行执行之后执行）
     */
    @PostConstruct
    public void init() {
        redisUtil = this;
        redisUtil.defaultRedisTemplate= this.defaultRedisTemplate;
        redisUtil.cacheRedisTemplate= this.cacheRedisTemplate;
        redisUtil.stringRedisTemplate = this.defaultRedisTemplate;

    }

    public void setDefaultRedisTemplate(){
        this.stringRedisTemplate=defaultRedisTemplate;
    }

    public void setCacheRedisTemplate(){
        this.stringRedisTemplate=cacheRedisTemplate;
    }



//endregion

    //region redis实例重启

    public void validateObject(final Object obj) {
        boolean closed = stringRedisTemplate.getConnectionFactory().getConnection().isClosed();
        if (closed) {
//            Jedis jedis = stringRedisTemplate.getConnectionFactory().getConnection().getNativeConnection();
            RedisConnection jedisConnection = jedisConnectionFactory.getConnection();
            Jedis jedis = (Jedis)jedisConnection.getNativeConnection();
        }
    }

    //endregion

    /** TODO ---------------------------------key相关操作---------------------------------------------- */
    //region key相关操作

    /**
     * 删除key(成功返回true)
     *
     * @param key 不能为空
     */
    public Boolean delete(String key) {
        stringRedisTemplate.delete(key);
        return true;
    }

    /**
     * 批量删除key(成功返回true)
     *
     * @param keys
     */
    public Boolean delete(Collection<String> keys) {
        stringRedisTemplate.delete(keys);
        return true;
    }

    /**
     * 序列化key
     *
     * @param key  不能为空
     * @return
     */
    public byte[] dump(String key) {
        return stringRedisTemplate.dump(key);
    }

    /**
     * 是否存在key(存在返回true)
     *
     * @param key  不能为空
     * @return
     */
    public Boolean hasKey(String key) {
        return stringRedisTemplate.hasKey(key);
    }

    /**
     * 设置过期时间
     *
     * @param key
     * @param timeout
     * @param unit
     * @return
     */
    public Boolean expire(String key, long timeout, TimeUnit unit) {
        return stringRedisTemplate.expire(key, timeout, unit);
    }

    /**
     * 设置过期时间
     *
     * @param key
     * @param date
     * @return
     */
    public Boolean expireAt(String key, Date date) {
        return stringRedisTemplate.expireAt(key, date);
    }

    /**
     * 查找匹配的key------不要用这个可以用SCAN 遍历查询所有的key,在生产环境中使用会导致，redis卡在这里
     * 获取所以key
     * @param pattern
     * @return
     */
    public Set<String> keys(String pattern) {
        return stringRedisTemplate.keys(pattern);
    }
    public Set<String> scan(String pattern){
        HashSet<String> set = new HashSet<>();
        RedisConnection connection = stringRedisTemplate.getConnectionFactory().getConnection();
        Cursor<byte[]> c = connection.scan(new ScanOptions.ScanOptionsBuilder().match(pattern).build());
        while (c.hasNext()) {
            set.add(new String(c.next()));
        }
        return set;
        /*stringRedisTemplate.execute(new RedisCallback<Set<Object>>() {
            @Override
            public Set<Object> doInRedis(RedisConnection connection) throws DataAccessException {
                Set<Object> binaryKeys = new HashSet<>();
                Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match("*").build());
                while (cursor.hasNext()) {
                    binaryKeys.add(new String(cursor.next()));
                    set.add(new String(cursor.next()));
                }
                return binaryKeys;
            }
        });
        return set;*/


    }


    /**
     * 将当前数据库的 key 移动到给定的数据库 db 当中
     *
     * @param key
     * @param dbIndex
     * @return
     */
    public Boolean move(String key, int dbIndex) {
        return stringRedisTemplate.move(key, dbIndex);
    }

    /**
     * 移除 key 的过期时间，key 将持久保持
     *
     * @param key
     * @return
     */
    public Boolean persist(String key) {
        return stringRedisTemplate.persist(key);
    }

    /**
     * 返回 key 的剩余的过期时间
     *
     * @param key
     * @param unit
     * @return
     */
    public Long getExpire(String key, TimeUnit unit) {
        return stringRedisTemplate.getExpire(key, unit);
    }

    /**
     * 返回 key 的剩余的过期时间
     *
     * @param key
     * @return
     */
    public Long getExpire(String key) {
        return stringRedisTemplate.getExpire(key);
    }

    /**
     * 从当前数据库中随机返回一个 key
     *
     * @return
     */
    public String randomKey() {
        return stringRedisTemplate.randomKey();
    }

    /**
     * 修改 key 的名称
     *
     * @param oldKey
     * @param newKey
     */
    public void rename(String oldKey, String newKey) {
        stringRedisTemplate.rename(oldKey, newKey);
    }

    /**
     * 仅当 newkey 不存在时，将 oldKey 改名为 newkey
     *
     * @param oldKey
     * @param newKey
     * @return
     */
    public Boolean renameIfAbsent(String oldKey, String newKey) {
        return stringRedisTemplate.renameIfAbsent(oldKey, newKey);
    }

    /**
     * 返回 key 所储存的值的类型
     *
     * @param key
     * @return
     */
    public DataType type(String key) {
        return stringRedisTemplate.type(key);
    }




    //endregion


    /**  TODO  ----------------------------------string相关操作------------------------------------- */
    //region string相关操作



    /**
     * 设置指定 key 的值
     * @param key
     * @param value
     * @param howManyDays 多少天
     */
    public void setByDays( String key , String value , long howManyDays  ){
        stringRedisTemplate.opsForValue().set(key, value, howManyDays, TimeUnit.DAYS);
    }

    /**
     * 设置指定 key 的值
     * @param key
     * @param value
     * @param howManySeconds 多少秒
     */
    public void setBySeconds( String key , String value , long howManySeconds ){
        stringRedisTemplate.opsForValue().set(key, value, howManySeconds, TimeUnit.SECONDS);
    }


    /**
     * 设置指定 key 的值
     * @param key
     * @param value
     */
    public void set(String key, String value) {
        stringRedisTemplate.opsForValue().set(key, value);
    }

    /**
     * 获取指定 key 的值
     * @param key
     * @return
     */
    public String get(String key) {
        String val = stringRedisTemplate.opsForValue().get(key);
        if (val == null) {
            return null;
        } else {
            return stringRedisTemplate.opsForValue().get(key).toString();
        }
    }

    /**
     * 返回 key 中字符串值的子字符(从0开始算)
     * @param key
     * @param start
     * @param end
     * @return
     */
    public String getRange(String key, long start, long end) {
        return stringRedisTemplate.opsForValue().get(key, start, end);
    }

    /**
     * 将给定 key 的值设为 value ，并返回 key 的旧值(old value)
     *
     * @param key
     * @param value
     * @return
     */
    public String getAndSet(String key, String value) {
        return stringRedisTemplate.opsForValue().getAndSet(key, value).toString();
    }

    /**
     * 对 key 所储存的字符串值，获取指定偏移量上的位(bit)
     *
     * @param key
     * @param offset
     * @return
     */
    public Boolean getBit(String key, long offset) {
        return stringRedisTemplate.opsForValue().getBit(key, offset);
    }

    /**
     * 批量获取
     *
     * @param keys
     * @return
     */
    public List<String> multiGet(Collection<String> keys) {
        return stringRedisTemplate.opsForValue().multiGet(keys);
    }

    /**
     * 设置ASCII码, 字符串'a'的ASCII码是97, 转为二进制是'01100001', 此方法是将二进制第offset位值变为value
     *
     * @param key
     * @ postion
     *            位置
     * @param value
     *            值,true为1, false为0
     * @return
     */
    public boolean setBit(String key, long offset, boolean value) {
        return stringRedisTemplate.opsForValue().setBit(key, offset, value);
    }

    /**
     * 将值 value 关联到 key ，并将 key 的过期时间设为 timeout
     *
     * @param key
     * @param value
     * @param timeout
     *            过期时间
     * @param unit
     *            时间单位, 天:TimeUnit.DAYS 小时:TimeUnit.HOURS 分钟:TimeUnit.MINUTES
     *            秒:TimeUnit.SECONDS 毫秒:TimeUnit.MILLISECONDS
     */
    public void setEx(String key, String value, long timeout, TimeUnit unit) {
        stringRedisTemplate.opsForValue().set(key, value, timeout, unit);
    }

    /**
     * 只有在 key 不存在时设置 key 的值
     * key唯一 如果key不存在 就返回false 并且无法插入
     * @param key
     * @param value
     * @return 之前已经存在返回false,不存在返回true
     */
    public boolean setIfAbsent(String key, String value) {
        return stringRedisTemplate.opsForValue().setIfAbsent(key, value);
    }

    /**
     * 用 value 参数覆写给定 key 所储存的字符串值，从偏移量 offset 开始
     * 替换key对应的String从offset位置开始的后面字段成value,如果不够就替换部分
     * 该方法是用 value 参数覆写(overwrite)给定 key 所储存的字符串值，从偏移量 offset 开始
     * @param key
     * @param value
     * @param offset
     *            从指定位置开始覆写
     */
    public void setRange(String key, String value, long offset) {
        stringRedisTemplate.opsForValue().set(key, value, offset);
    }

    /**
     * 获取字符串的长度
     *
     * @param key
     * @return
     */
    public Long size(String key) {
        return stringRedisTemplate.opsForValue().size(key);
    }

    /**
     * 批量添加
     *
     * @param maps
     */
    public void multiSet(Map<String, String> maps) {
        stringRedisTemplate.opsForValue().multiSet(maps);
    }

    /**
     * 同时设置一个或多个 key-value 对，当且仅当所有给定 key 都不存在
     *
     * @param maps
     * @return 之前已经存在返回false,不存在返回true
     */
    public boolean multiSetIfAbsent(Map<String, String> maps) {
        return stringRedisTemplate.opsForValue().multiSetIfAbsent(maps);
    }

    /**
     * 正数为增加, 负数则为减多少，不写是(自增长)
     *
     * @param key
     * @ value
     * @return
     */
    public Long incrBy(String key, long increment) {
        return stringRedisTemplate.opsForValue().increment(key, increment);
    }

    /**
     * 正数为增加, 负数则为减多少
     * @param key
     * @ value
     * @return
     */
    public Double incrByFloat(String key, double increment) {
        return stringRedisTemplate.opsForValue().increment(key, increment);
    }

    /**
     * 追加到末尾
     *
     * @param key
     * @param value
     * @return
     */
    public Integer append(String key, String value) {
        return stringRedisTemplate.opsForValue().append(key, value);
    }


    //endregion


    /** TODO  ---------------------------------------hash相关操作-------------------------------------------- */
    //region hash相关操作

    /**
     * 获取存储在哈希表中指定字段的值
     *
     * @param key
     * @param field
     * @return
     */
    public Object hGet(String key, String field) {
        return stringRedisTemplate.opsForHash().get(key, field);
    }

    /**
     * 获取所有给定字段的值
     *
     * @param key
     * @return {key2=b, key1=a, key3=c}获取hsah内容
     */
    public Map<Object, Object> hGetAll(String key) {
        return stringRedisTemplate.opsForHash().entries(key);
    }

    /**
     * 获取所有给定字段的值
     *
     * @param key
     * @param fields
     * @return
     */
    public List<Object> hMultiGet(String key, Collection<Object> fields) {
        return stringRedisTemplate.opsForHash().multiGet(key, fields);
    }

    /**
     * 为散列添加或者覆盖一个 key-value键值对
     */
    public void hPut(String key, String hashKey, String value) {
        stringRedisTemplate.opsForHash().put(key, hashKey, value);
    }

    /**
     * 为散列添加多个key-value键值对
     *
     * @ key must not be {@literal null}.
     * @ m must not be {@literal null}.
     */
    public void hPutAll(String key, Map<String, String> maps) {
        stringRedisTemplate.opsForHash().putAll(key, maps);
    }

    /**
     * 仅当hashKey不存在时才设置
     * 为散列添加一个key-value键值对。如果存在则不添加不覆盖。返回false
     *
     * @param key
     * @param hashKey
     * @param value
     * @return
     */
    public Boolean hPutIfAbsent(String key, String hashKey, String value) {
        return stringRedisTemplate.opsForHash().putIfAbsent(key, hashKey, value);
    }

    /**
     * 删除一个或多个哈希表字段
     *
     * @param key 不能为null 散列的名称
     * @param fields 需要删除的keys集合
     * @return
     */
    public Long hDelete(String key, Object... fields) {
        return stringRedisTemplate.opsForHash().delete(key, fields);
    }

    /**
     * 查看哈希表 key 中，指定的字段是否存在
     *
     * @param key
     * @param field
     * @return
     */
    public boolean hExists(String key, String field) {
        return stringRedisTemplate.opsForHash().hasKey(key, field);
    }

    /**
     * 为哈希表 key 中的指定字段的整数值加上增量 increment
     *
     * @param key
     * @param field
     * @param increment
     * @return
     */
    public Long hIncrBy(String key, Object field, long increment) {
        return stringRedisTemplate.opsForHash().increment(key, field, increment);
    }

    /**
     * 为哈希表 key 中的指定字段的整数值加上增量 increment
     *
     * @param key
     * @param field
     * @param delta
     * @return
     */
    public Double hIncrByFloat(String key, Object field, double delta) {
        return stringRedisTemplate.opsForHash().increment(key, field, delta);
    }

    /**
     * 获取所有哈希表中的字段
     *
     * @param key
     * @return
     */
    public Set<Object> hKeys(String key) {
        return stringRedisTemplate.opsForHash().keys(key);
    }

    /**
     * 获取哈希表中字段的数量
     *
     * @param key
     * @return
     */
    public Long hSize(String key) {
        return stringRedisTemplate.opsForHash().size(key);
    }

    /**
     * 获取哈希表中所有值
     *
     * @param key
     * @return
     */
    public List<Object> hValues(String key) {
        return stringRedisTemplate.opsForHash().values(key);
    }

    /**
     * 迭代哈希表中的键值对
     *
     * @param key
     * @param options
     * @return
     */
    public Cursor<Map.Entry<Object, Object>> hScan(String key, ScanOptions options) {
        return stringRedisTemplate.opsForHash().scan(key, options);
    }
    //endregion

    /** TODO  ---------------------------------list相关操作---------------------------------------- */
    //region list相关操作

    /**
     * 通过索引获取列表中的元素
     * 当索引参数超出范围，或对一个空列表进行 LSET 时，返回一个错误。
     * @param key
     * @param index
     * @return
     */
    public String lIndex(String key, long index) {
        return stringRedisTemplate.opsForList().index(key, index).toString();
    }

    /**
     * 获取列表指定范围内的元素
     *  返回列表中指定区间内的元素，区间以偏移量 START 和 END 指定。 其中 0 表示列表的第一个元素， 1 表示列表的第二个元素，以此类推。
     *   你也可以使用负数下标，以 -1 表示列表的最后一个元素， -2 表示列表的倒数第二个元素，以此类推。
     * @param key
     * @param start
     *            开始位置, 0是开始位置
     * @param end
     *            结束位置, -1返回所有
     * @return
     */
    public List<String> lRange(String key, long start, long end) {
        return stringRedisTemplate.opsForList().range(key, start, end);
    }

    /**
     * 存储在list头部
     * left 是左面进入 也就是靠前 先进后出  返回的是list的数量从1开始
     * @param key
     * @param value
     * @return
     */
    public Long lLeftPush(String key, String value) {
        return stringRedisTemplate.opsForList().leftPush(key, value);
    }

    /**
     * 将数组倒过来添加进去
     * @param key
     * @param value
     * @return
     */
    public Long lLeftPushAll(String key, String... value) {
        return stringRedisTemplate.opsForList().leftPushAll(key, value);
    }

    /**
     * 将集合倒过来添加进去
     * @param key
     * @param value
     * @return
     */
    public Long lLeftPushAll(String key, Collection<String> value) {
        return stringRedisTemplate.opsForList().leftPushAll(key, value);
    }

    /**
     * 当list存在的时候才加入
     * 把值加入列表的头部 必须有一个值。没有值无效
     * @param key
     * @param value
     * @return
     */
    public Long lLeftPushIfPresent(String key, String value) {
        return stringRedisTemplate.opsForList().leftPushIfPresent(key, value);
    }

    /**
     * 如果pivot存在,再pivot前面添加
     * 把值插入到指定元素的前面
     * 输入 0 1 50 然后把 2插入到0前面 就是 50 1 2 0
     * @param key
     * @param pivot
     * @param value
     * @return
     */
    public Long lLeftPush(String key, String pivot, String value) {
        return stringRedisTemplate.opsForList().leftPush(key, pivot, value);
    }

    /**
     * 按顺序添加
     * @param key
     * @param value
     * @return
     */
    public Long lRightPush(String key, String value) {
        return stringRedisTemplate.opsForList().rightPush(key, value);
    }

    /**
     * 按顺序添加数组
     * @param key
     * @param value
     * @return
     */
    public Long lRightPushAll(String key, String... value) {
        return stringRedisTemplate.opsForList().rightPushAll(key, value);
    }

    /**
     * 按顺序添加集合
     * @param key
     * @param value
     * @return
     */
    public Long lRightPushAll(String key, Collection<String> value) {
        return stringRedisTemplate.opsForList().rightPushAll(key, value);
    }

    /**
     * 为已存在的列表添加值
     * 必须有一个key 否则无效 然后存在之后继续向后插值
     * @param key
     * @param value
     * @return
     */
    public Long lRightPushIfPresent(String key, String value) {
        return stringRedisTemplate.opsForList().rightPushIfPresent(key, value);
    }

    /**
     * 在pivot元素的右边添加值
     * 指定Value后面插入值
     * @param key
     * @param pivot
     * @param value
     * @return
     */
    public Long lRightPush(String key, String pivot, String value) {
        return stringRedisTemplate.opsForList().rightPush(key, pivot, value);
    }

    /**
     * 通过索引设置列表元素的值
     * 当索引参数超出范围，或对一个空列表进行 LSET 时，返回一个错误。
     * @param key
     * @param index
     *            位置
     * @param value
     */
    public void lSet(String key, long index, String value) {
        stringRedisTemplate.opsForList().set(key, index, value);
    }

    /**
     * 移出并获取列表的第一个元素
     *
     * @param key
     * @return 删除的元素
     */
    public String lLeftPop(String key) {
        return stringRedisTemplate.opsForList().leftPop(key).toString();
    }

    /**
     * 移出并获取列表的第一个元素， 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
     *
     * @param key
     * @param timeout
     *            等待时间
     * @param unit
     *            时间单位
     * @return
     */
    public String lBLeftPop(String key, long timeout, TimeUnit unit) {
        return stringRedisTemplate.opsForList().leftPop(key, timeout, unit).toString();
    }

    /**
     * 移除并获取列表最后一个元素
     *
     * @param key
     * @return 删除的元素
     */
    public String lRightPop(String key) {
        return stringRedisTemplate.opsForList().rightPop(key).toString();
    }

    /**
     * 移出并获取列表的最后一个元素， 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
     *
     * @param key
     * @param timeout
     *            等待时间
     * @param unit
     *            时间单位
     * @return
     */
    public String lBRightPop(String key, long timeout, TimeUnit unit) {
        return stringRedisTemplate.opsForList().rightPop(key, timeout, unit).toString();
    }

    /**
     * 移除列表的最后一个元素，并将该元素添加到另一个列表并返回
     *
     * @param sourceKey
     * @param destinationKey
     * @return
     */
    public String lRightPopAndLeftPush(String sourceKey, String destinationKey) {
        return stringRedisTemplate.opsForList().rightPopAndLeftPush(sourceKey,
                destinationKey).toString();
    }

    /**
     * 从列表中弹出一个值，将弹出的元素插入到另外一个列表中并返回它； 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
     *
     * @param sourceKey
     * @param destinationKey
     * @param timeout
     * @param unit
     * @return
     */
    public String lBRightPopAndLeftPush(String sourceKey, String destinationKey,
                                        long timeout, TimeUnit unit) {
        return stringRedisTemplate.opsForList().rightPopAndLeftPush(sourceKey,
                destinationKey, timeout, unit).toString();
    }

    /**
     * 删除集合中值等于value得元素
     *
     * @param key
     * @param index
     *            index=0, 删除所有值等于value的元素; index>0, 从头部开始删除第一个值等于value的元素;
     *            index<0, 从尾部开始删除第一个值等于value的元素;
     * @param value
     * @return
     */
    public Long lRemove(String key, long index, String value) {
        return stringRedisTemplate.opsForList().remove(key, index, value);
    }

    /**
     * 裁剪list
     *
     * @param key
     * @param start
     * @param end
     */
    public void lTrim(String key, long start, long end) {
        stringRedisTemplate.opsForList().trim(key, start, end);
    }

    /**
     * 获取列表长度
     *
     * @param key
     * @return
     */
    public Long lSize(String key) {
        return stringRedisTemplate.opsForList().size(key);
    }
    //endregion

    /** TODO  ------------------------------------------set相关操作--------------------------------------------------------- */
    //region set相关操作

    /**
     * set添加元素
     *
     * @param key
     * @param values
     * @return
     */
    public Long sAdd(String key, String... values) {
        return stringRedisTemplate.opsForSet().add(key, values);
    }
    /*//循环填充数组的方法 wbh 2019-5-17
    public Long sAdd(String key, List<Integer> list) {
        if (list != null && list.size() > 0) {
            Integer[] values = new Integer[list.size()];
            ;
            for (int i = 0; i < list.size(); i++) {
                values[i] = list.get(0);
            }
            return stringRedisTemplate.opsForSet().add(key, values);
        }
        return 0L;
    }

    //递归的方法 wbh 2019-5-17
    public Long sAdd2(String key, List<Integer> list, Integer... values ) {

        if (list != null && list.size() > 0) {
            Integer value = list.get(0);
            list.remove(0);
            int length=values.length;
            Integer[] values2=new Integer[length+1];;
            System.arraycopy(values,0, values2,0,length);
            values2[length]=value;
            return sAdd2(key, list, values2);
        } else {
            return stringRedisTemplate.opsForSet().add(key, values);
        }
    }*/

    /**
     * 向redis的Set数据类型中添加集合对象
     * @param key
     * @param list
     * @param values
     * @return
     */
    public Long sAddCollection(String key, Collection<? extends String> list, String... values ) {
        if (list != null && list.size() > 0) {
            values=list.toArray(new String[list.size()]);
            return stringRedisTemplate.opsForSet().add(key,values);
        }
        return  0L;
    }



    /**
     * set移除元素
     *
     * @param key
     * @param values
     * @return
     */
    public Long sRemove(String key, Object... values) {
        return stringRedisTemplate.opsForSet().remove(key, values);
    }

    /**
     * 移除并返回集合的一个随机元素
     *
     * @param key
     * @return
     */
    public String sPop(String key) {
        return stringRedisTemplate.opsForSet().pop(key).toString();
    }

    /**
     * 将元素value从一个集合移到另一个集合
     *
     * @param key
     * @param value
     * @param destKey
     * @return
     */
    public Boolean sMove(String key, String value, String destKey) {
        return stringRedisTemplate.opsForSet().move(key, value, destKey);
    }

    /**
     * 获取集合的大小
     *
     * @param key
     * @return
     */
    public Long sSize(String key) {
        return stringRedisTemplate.opsForSet().size(key);
    }

    /**
     * 判断集合是否包含value
     *
     * @param key
     * @param value
     * @return
     */
    public Boolean sIsMember(String key, Object value) {
        return stringRedisTemplate.opsForSet().isMember(key, value);
    }

    /**
     * 获取两个集合的交集
     *
     * @param key
     * @param otherKey
     * @return
     */
    public Set<String> sIntersect(String key, String otherKey) {
        return stringRedisTemplate.opsForSet().intersect(key, otherKey);
    }

    /**
     * 获取key集合与多个集合的交集
     *
     * @param key
     * @param otherKeys
     * @return
     */
    public Set<String> sIntersect(String key, Collection<String> otherKeys) {
        return stringRedisTemplate.opsForSet().intersect(key, otherKeys);
    }

    /**
     * key集合与otherKey集合的交集存储到destKey集合中
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long sIntersectAndStore(String key, String otherKey, String destKey) {
        return stringRedisTemplate.opsForSet().intersectAndStore(key, otherKey,
                destKey);
    }

    /**
     * key集合与多个集合的交集存储到destKey集合中
     *
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Long sIntersectAndStore(String key, Collection<String> otherKeys,
                                   String destKey) {
        return stringRedisTemplate.opsForSet().intersectAndStore(key, otherKeys,
                destKey);
    }

    /**
     * 获取两个集合的并集
     *
     * @param key
     * @param otherKeys
     * @return
     */
    public Set<String> sUnion(String key, String otherKeys) {
        return stringRedisTemplate.opsForSet().union(key, otherKeys);
    }

    /**
     * 获取key集合与多个集合的并集
     *
     * @param key
     * @param otherKeys
     * @return
     */
    public Set<String> sUnion(String key, Collection<String> otherKeys) {
        return stringRedisTemplate.opsForSet().union(key, otherKeys);
    }

    /**
     * key集合与otherKey集合的并集存储到destKey中
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long sUnionAndStore(String key, String otherKey, String destKey) {
        return stringRedisTemplate.opsForSet().unionAndStore(key, otherKey, destKey);
    }

    /**
     * key集合与多个集合的并集存储到destKey中
     *
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Long sUnionAndStore(String key, Collection<String> otherKeys,
                               String destKey) {
        return stringRedisTemplate.opsForSet().unionAndStore(key, otherKeys, destKey);
    }

    /**
     * 获取两个集合的差集
     *
     * @param key
     * @param otherKey
     * @return
     */
    public Set<String> sDifference(String key, String otherKey) {
        return stringRedisTemplate.opsForSet().difference(key, otherKey);
    }

    /**
     * 获取key集合与多个集合的差集
     *
     * @param key
     * @param otherKeys
     * @return
     */
    public Set<String> sDifference(String key, Collection<String> otherKeys) {
        return stringRedisTemplate.opsForSet().difference(key, otherKeys);
    }

    /**
     * key集合与otherKey集合的差集存储到destKey中
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long sDifference(String key, String otherKey, String destKey) {
        return stringRedisTemplate.opsForSet().differenceAndStore(key, otherKey,
                destKey);
    }

    /**
     * key集合与多个集合的差集存储到destKey中
     *
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Long sDifference(String key, Collection<String> otherKeys,
                            String destKey) {
        return stringRedisTemplate.opsForSet().differenceAndStore(key, otherKeys,
                destKey);
    }

    /**
     * 获取集合所有元素
     *
     * @param key
     * @ otherKeys
     * @ destKey
     * @return
     */
    public Set<String> setMembers(String key) {
        return stringRedisTemplate.opsForSet().members(key);
    }

    /**
     * 随机获取集合中的一个元素
     *
     * @param key
     * @return
     */
    public String sRandomMember(String key) {
        return stringRedisTemplate.opsForSet().randomMember(key).toString();
    }

    /**
     * 随机获取集合中count个元素
     *
     * @param key
     * @param count
     * @return
     */
    public List<String> sRandomMembers(String key, long count) {
        return stringRedisTemplate.opsForSet().randomMembers(key, count);
    }

    /**
     * 随机获取集合中count个元素并且去除重复的
     *
     * @param key
     * @param count
     * @return
     */
    public Set<String> sDistinctRandomMembers(String key, long count) {
        return stringRedisTemplate.opsForSet().distinctRandomMembers(key, count);
    }

    /**
     * 遍历
     * @param key
     * @param options
     * @return
     */
    public Cursor<String> sScan(String key, ScanOptions options) {
        return stringRedisTemplate.opsForSet().scan(key, options);
    }
    //endregion

    /** TODO ----------------------------------zSet相关操作-------------------------------------------------*/
    //region zSet相关操作

    /**
     * 添加元素,有序集合是按照元素的score值由小到大排列
     *
     * @param key
     * @param value
     * @param score
     * @return
     */
    public Boolean zAdd(String key, String value, double score) {
        return stringRedisTemplate.opsForZSet().add(key, value, score);
    }

    /**
     * 添加set对象
     * @param key
     * @param values
     * @return
     */
    public Long zAdd(String key, Set<ZSetOperations.TypedTuple<String>> values) {
        return stringRedisTemplate.opsForZSet().add(key, values);
    }

    /**
     * 移除有序集合中指定的多个成员. 如果成员不存在则忽略
     * @param key
     * @param values
     * @return
     */
    public Long zRemove(String key, Object... values) {
        return stringRedisTemplate.opsForZSet().remove(key, values);
    }

    /**
     * 增加元素的score值，并返回增加后的值
     *
     * @param key
     * @param value
     * @param delta
     * @return
     */
    public Double zIncrementScore(String key, String value, double delta) {
        return stringRedisTemplate.opsForZSet().incrementScore(key, value, delta);
    }

    /**
     * 返回元素在集合的排名,有序集合是按照元素的score值由小到大排列
     *
     * @param key
     * @param value
     * @return 0表示第一位
     */
    public Long zRank(String key, Object value) {
        return stringRedisTemplate.opsForZSet().rank(key, value);
    }

    /**
     * 返回元素在集合的排名,按元素的score值由大到小排列
     *
     * @param key
     * @param value
     * @return
     */
    public Long zReverseRank(String key, Object value) {
        return stringRedisTemplate.opsForZSet().reverseRank(key, value);
    }

    /**
     * 获取集合的元素, 从小到大排序
     *
     * @param key
     * @param start
     *            开始位置
     * @param end
     *            结束位置, -1查询所有
     * @return
     */
    public Set<String> zRange(String key, long start, long end) {
        return stringRedisTemplate.opsForZSet().range(key, start, end);
    }

    /**
     * 获取集合元素, 并且把score值也获取
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<ZSetOperations.TypedTuple<String>> zRangeWithScores(String key, long start, long end) {
        return stringRedisTemplate.opsForZSet().rangeWithScores(key, start, end);
    }

    /**
     * 根据Score值查询集合元素
     *
     * @param key
     * @param min
     *            最小值
     * @param max
     *            最大值
     * @return
     */
    public Set<String> zRangeByScore(String key, double min, double max) {
        return stringRedisTemplate.opsForZSet().rangeByScore(key, min, max);
    }

    /**
     * 根据Score值查询集合元素, 从小到大排序
     *
     * @param key
     * @param min
     *            最小值
     * @param max
     *            最大值
     * @return
     */
    public Set<ZSetOperations.TypedTuple<String>> zRangeByScoreWithScores(String key,
                                                                          double min, double max) {
        return stringRedisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max);
    }

    /**
     * 获取有序集合中分数在指定的最小值 与最大值之间的所有成员的TypedTuple集合  闭合区间
     * 通过分数返回有序集合指定区间内的成员对象，并在索引范围内，其中有序集成员按分数值递增(从小到大)顺序排列
     * 从开始到结束的范围内获取一组元组，其中分数在分类集中的最小值和最大值之间。
     * @param key
     * @param min
     * @param max
     * @param start
     * @param end
     * @return
     */
    public Set<ZSetOperations.TypedTuple<String>> zRangeByScoreWithScores(String key,
                                                                          double min, double max, long start, long end) {
        return stringRedisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max,
                start, end);
    }

    /**
     * 获取集合的元素, 从大到小排序
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<String> zReverseRange(String key, long start, long end) {
        return stringRedisTemplate.opsForZSet().reverseRange(key, start, end);
    }

    /**
     * 获取集合的元素, 从大到小排序, 并返回score值
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<ZSetOperations.TypedTuple<String>> zReverseRangeWithScores(String key,
                                                                          long start, long end) {
        return stringRedisTemplate.opsForZSet().reverseRangeWithScores(key, start,
                end);
    }

    /**
     * 根据Score值查询集合元素, 从大到小排序
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Set<String> zReverseRangeByScore(String key, double min,
                                            double max) {
        return stringRedisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
    }

    /**
     * 根据Score值查询集合元素, 从大到小排序
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Set<ZSetOperations.TypedTuple<String>> zReverseRangeByScoreWithScores(
            String key, double min, double max) {
        return stringRedisTemplate.opsForZSet().reverseRangeByScoreWithScores(key,
                min, max);
    }

    /**
     * 从有序集合中获取分数在指定范围内从高到低的成员集合
     * @param key
     * @param min
     * @param max
     * @param start
     * @param end
     * @return
     */
    public Set<String> zReverseRangeByScore(String key, double min,
                                            double max, long start, long end) {
        return stringRedisTemplate.opsForZSet().reverseRangeByScore(key, min, max,
                start, end);
    }

    /**
     * 根据score值获取集合元素数量
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Long zCount(String key, double min, double max) {
        return stringRedisTemplate.opsForZSet().count(key, min, max);
    }

    /**
     * 获取集合大小
     *
     * @param key
     * @return
     */
    public Long zSize(String key) {
        return stringRedisTemplate.opsForZSet().size(key);
    }

    /**
     * 获取集合大小
     *
     * @param key
     * @return
     */
    public Long zZCard(String key) {
        return stringRedisTemplate.opsForZSet().zCard(key);
    }

    /**
     * 获取集合中value元素的score值
     *
     * @param key
     * @param value
     * @return
     */
    public Double zScore(String key, Object value) {
        Double score = stringRedisTemplate.opsForZSet().score(key, value);
        return score;
    }

    /**
     * 移除指定索引位置的成员
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Long zRemoveRange(String key, long start, long end) {
        return stringRedisTemplate.opsForZSet().removeRange(key, start, end);
    }

    /**
     * 根据指定的score值的范围来移除成员
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Long zRemoveRangeByScore(String key, double min, double max) {
        return stringRedisTemplate.opsForZSet().removeRangeByScore(key, min, max);
    }

    /**
     * 获取key和otherKey的并集并存储在destKey中
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long zUnionAndStore(String key, String otherKey, String destKey) {
        return stringRedisTemplate.opsForZSet().unionAndStore(key, otherKey, destKey);
    }

    /**
     * 求两个有序集合的并集，并存到目标集合中。 如果存在相同的成员，则分数相加。
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Long zUnionAndStore(String key, Collection<String> otherKeys,
                               String destKey) {
        return stringRedisTemplate.opsForZSet()
                .unionAndStore(key, otherKeys, destKey);
    }

    /**
     * 交集
     * 一个有序集合与多个有序集合进行相同成员并集， 如果存在不相同相同成员，则忽略
     * @param key 不能为null
     * @param otherKey 不能为null
     * @param destKey 不能为null
     * @return 有交集时为正数，没有时为0
     */
    public Long zIntersectAndStore(String key, String otherKey,
                                   String destKey) {
        return stringRedisTemplate.opsForZSet().intersectAndStore(key, otherKey,
                destKey);
    }

    /**
     * 求两个有序集合中相同成员的并集（分数相加），并存到目标集合中。没有共同的成员则忽略
     * (交集)
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Long zIntersectAndStore(String key, Collection<String> otherKeys,
                                   String destKey) {
        return stringRedisTemplate.opsForZSet().intersectAndStore(key, otherKeys,
                destKey);
    }

    /**
     * 遍历
     * @param key
     * @param options
     * @return
     */
    public Cursor<ZSetOperations.TypedTuple<String>> zScan(String key, ScanOptions options) {
        return stringRedisTemplate.opsForZSet().scan(key, options);
    }

    //endregion

    //TODO------------------------------------------排序（正序、倒序）--------------------------------------------
    //region 排序（正序、倒序）

    /**
     * ZSet对象根据传入key值和参数获取对应分页排序的数据----优化方向可以应用rangeByScoreWithScores和reverseRangeByScoreWithScores直接进行分页
     * @param key
     * @param sortDirection 排序方法：true为正序即从小到大；false为倒序即为从大到小（排序的关键是根据分数进行排序）
     * @param paging 是否分页
     * @param currentPage 当前页面（即获取第几页的数据）
     * @param pageNum  每页显示数据量
     * @return
     */
    public List<Object> pageAndSort(String key, Boolean sortDirection, Boolean paging , Integer currentPage, Integer pageNum) {
        List<Object> list = new ArrayList<>();
        currentPage = currentPage==null?0:currentPage-1;
        pageNum = pageNum==null?3:pageNum;
        sortDirection = sortDirection==null?true:sortDirection;
        paging = paging==null?false:paging;

//        String tempListKey = "Temp:opsForZSet:pageAndSort";
//        stringRedisTemplate.delete(tempListKey);




        //根据分数内容进行正序或倒叙的List获取
        if (sortDirection){
//            LinkedHashSet<Object> set = (LinkedHashSet<Object>)stringRedisTemplate.opsForZSet().range(key, 0, -1);
            Set<ZSetOperations.TypedTuple<String>> tuples = stringRedisTemplate.opsForZSet().rangeByScoreWithScores(key,0, 1000000, currentPage * pageNum, pageNum);
            List<Object> list_1 = new ArrayList<>(tuples);
            for (ZSetOperations.TypedTuple<String> tup:tuples) {
                list_1.add(tup.getValue());
            }
            list.addAll(list_1);
//            stringRedisTemplate.opsForList().rightPushAll(tempListKey,list_1);
        }else {
//            LinkedHashSet<Object> tuples = (LinkedHashSet<Object>)stringRedisTemplate.opsForZSet().reverseRange(key, 0, -1);
            Set<ZSetOperations.TypedTuple<String>> tuples = stringRedisTemplate.opsForZSet().reverseRangeByScoreWithScores(key,0, 1000000, currentPage * pageNum, pageNum);
            List<Object> list_2 = new ArrayList<>();
            for (ZSetOperations.TypedTuple<String> tup:tuples) {
                list_2.add(tup.getValue());
            }
            list.addAll(list_2);
//            stringRedisTemplate.opsForList().rightPushAll(tempListKey,list_2);
        }
//        list = stringRedisTemplate.opsForList().range(tempListKey,0,-1);

        //如果是进行分页排序
//        if (paging){
//            list = stringRedisTemplate.opsForList().range(tempListKey, currentPage * pageNum, (currentPage + 1) * pageNum-1);
//        }
//        stringRedisTemplate.delete(tempListKey);
        return list;
    }
    //endregion

    //TODO--------------------------------------------ZSet日常----------------------------------------------
    //region ZSet日常

    /**
     * 日常 ------ZSet
     * 交集方法
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Set<String> dailyIntersectAndStore(String key, Collection<String> otherKeys,
                                              String destKey) {
        stringRedisTemplate.opsForZSet().intersectAndStore(key, otherKeys, destKey);
        return stringRedisTemplate.opsForZSet().range(destKey,0,-1);
    }


    /**
     * 日常 Set ------ZSet
     * 并集方法
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Set<String> dailyUnionAndStore(String key, Collection<String> otherKeys, String destKey) {
        stringRedisTemplate.opsForZSet().unionAndStore(key, otherKeys, destKey);
        return stringRedisTemplate.opsForZSet().range(destKey,0,-1);
    }

    /**
     * 日常 Set ------ZSet
     * 差集方法
     * @param key
     * @param otherKeys
     * @param destKey
     * @return     返回的值为key中有，otherKeys中没有的value
     */
    public Set<String> dailyDifference(String key, Collection<String> otherKeys, String destKey) {

        stringRedisTemplate.delete("Temp:ZSet:diTemp1");
        stringRedisTemplate.delete("Temp:ZSet");
        ArrayList<String> list = new ArrayList<>();
        Set<String> diTemp1Set = dailyIntersectAndStore(key, list, "Temp:ZSet:diTemp1");
        for (int i = 0;i<=otherKeys.size();i++){
            stringRedisTemplate.opsForZSet().remove("Temp:ZSet:diTemp1",stringRedisTemplate.opsForZSet().range(otherKeys.toArray()[i].toString(),0,-1).toArray());
        }
        stringRedisTemplate.opsForZSet().unionAndStore("Temp:ZSet:diTemp1","Temp:ZSet",destKey);
        return stringRedisTemplate.opsForZSet().range(destKey,0,-1);
    }
    //endregion


    //设置字符串
    public ValueOperations<String, String> opsForValue(){
        return stringRedisTemplate.opsForValue();
    }

    public void deleteByPrex(String prex) {
        // 某个开头的key开头的redis数据
        Set<String> keys = stringRedisTemplate.keys(prex+"*");
        if (CollectionUtils.isNotEmpty(keys)) {
            stringRedisTemplate.delete(keys);
        }
    }

}
